package ws

import (
	"encoding/json"
	"github.com/google/uuid"
	"github.com/gorilla/websocket"
	"net/http"
	"time"
)

func NewConn(w http.ResponseWriter, r *http.Request) (conn *websocket.Conn, err error) {
	return _upgrader.Upgrade(w, r, nil)
}

func NewClient(hub *Hub, conn *websocket.Conn, uid string, groups []string, fr, fw func(*Client, *Message) error) *Client {
	if len(uid) == 0 {
		uid = uuid.NewString()
	}
	client := &Client{
		Id:     uid,
		Groups: groups,
		Hub:    hub,
		Conn:   conn,
		Send:   make(chan *Message, 256),
	}
	hub.Register <- client
	go client.writePump(fw)
	go client.readPump(fr)
	return client
}

func (c *Client) readPump(fn func(*Client, *Message) error) {
	defer func() {
		c.Hub.Unregister <- c
		_ = c.Conn.Close()
	}()
	c.Conn.SetReadLimit(maxMessageSize)
	_ = c.Conn.SetReadDeadline(time.Now().Add(pongWait))
	c.Conn.SetPongHandler(func(string) error {
		_ = c.Conn.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})
	for {
		_, data, err := c.Conn.ReadMessage()
		if err != nil {
			break
		}
		msg := &Message{}
		_ = json.Unmarshal(data, msg)
		if msg.Content == nil {
			msg.Content = string(data)
		}
		msg.Sender = c.Id
		if fn == nil || fn(c, msg) == nil {
			c.Hub.Broadcast <- msg
		}
	}
}

func (c *Client) writePump(fn func(*Client, *Message) error) {
	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
		_ = c.Conn.Close()
	}()
	for {
		select {
		case msg, ok := <-c.Send:
			if !ok {
				_ = c.Conn.WriteMessage(websocket.CloseMessage, nil)
				return
			}
			if fn == nil || fn(c, msg) == nil {
				_ = c.Conn.SetWriteDeadline(time.Now().Add(writeWait))
				_ = c.Conn.WriteJSON(msg.Content)
			}
		case <-ticker.C:
			_ = c.Conn.SetWriteDeadline(time.Now().Add(writeWait))
			if c.Conn.WriteMessage(websocket.PingMessage, nil) != nil {
				return
			}
		}
	}
}
